
`include "riscv_defs.v"

module riscv_alu(
    input [3:0]  aluop,
    input [31:0] in_a,
    input [31:0] in_b,

    output [31:0] out,
    output zero,
    output reg less);

reg [31:0] result;

reg [31:16]     shift_right_fill_r;
reg [31:0]      shift_right_1_r;
reg [31:0]      shift_right_2_r;
reg [31:0]      shift_right_4_r;
reg [31:0]      shift_right_8_r;

reg [31:0]      shift_left_1_r;
reg [31:0]      shift_left_2_r;
reg [31:0]      shift_left_4_r;
reg [31:0]      shift_left_8_r;

wire [31:0]     sub_res_w = in_a - in_b;
	
always @(*) begin
    less = 1'b0;
	case (aluop)
	`ALU_SHIFTL: begin
		if (in_b[0] == 1'b1)
			shift_left_1_r = {in_a[30:0],1'b0};
		else
			shift_left_1_r = in_a;

		if (in_b[1] == 1'b1)
			shift_left_2_r = {shift_left_1_r[29:0],2'b00};
		else
			shift_left_2_r = shift_left_1_r;

		if (in_b[2] == 1'b1)
			shift_left_4_r = {shift_left_2_r[27:0], 4'b0000};
		else
			shift_left_4_r = shift_left_2_r;

		if (in_b[3] == 1'b1)
			shift_left_8_r = {shift_left_4_r[23:0], 8'b00000000};
		else
			shift_left_8_r = shift_left_4_r;

		if (in_b[4] == 1'b1)
			result = {shift_left_8_r[15:0], 16'b0000000000000000};
		else
			result = shift_left_8_r;
	end

	`ALU_SHIFTR, `ALU_SHIFTR_ARITH: begin
		if (in_a[31] == 1'b1 && aluop == `ALU_SHIFTR_ARITH)
			shift_right_fill_r = 16'b1111111111111111;
		else
			shift_right_fill_r = 16'b0000000000000000;

		if (in_b[0] == 1'b1)
			shift_right_1_r = {shift_right_fill_r[31], in_a[31:1]};
		else
			shift_right_1_r = in_a;

		if (in_b[1] == 1'b1)
			shift_right_2_r = {shift_right_fill_r[31:30], shift_right_1_r[31:2]};
		else
			shift_right_2_r = shift_right_1_r;

		if (in_b[2] == 1'b1)
			shift_right_4_r = {shift_right_fill_r[31:28], shift_right_2_r[31:4]};
		else
			shift_right_4_r = shift_right_2_r;

		if (in_b[3] == 1'b1)
			shift_right_8_r = {shift_right_fill_r[31:24], shift_right_4_r[31:8]};
		else
			shift_right_8_r = shift_right_4_r;

		if (in_b[4] == 1'b1)
			result = {shift_right_fill_r[31:16], shift_right_8_r[31:16]};
		else
			result = shift_right_8_r;
       end       

	`ALU_LESS: 
	begin
		if (in_a < in_b) begin
			result = 32'h1;
			less = 1'b1;
		end else begin
			result = 32'h0;
			less = 1'b0;
		end
	end

	`ALU_LESS_SIGNED: 
	begin
		if (in_a[31] != in_b[31]) begin
			less = in_a[31] ? 1'b1 : 1'b0;
			result = in_a[31] ? 32'h1 : 32'h0;
		end else begin
			less = sub_res_w[31] ? 1'b1 : 1'b0;            
			result = sub_res_w[31] ? 32'h1 : 32'h0;
		end
	end 

	`ALU_ADD: result = in_a + in_b;
	`ALU_SUB: result = in_a - in_b;
	`ALU_AND: result = in_a & in_b;
	`ALU_OR:  result = in_a | in_b;
	`ALU_XOR: result = in_a ^ in_b;
	`ALU_OUTB: result = in_b;
	default:  result = in_a;
	endcase
end

assign out = result;
assign zero = (in_a == in_b ? 1 : 0);

endmodule

